/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.lm.base.eao;

import br.com.lm.base.util.JPAUtil;
import br.com.lm.base.util.ListenerContextoEM;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpSession;
import org.hibernate.CacheMode;

public abstract class AbstractEao<T extends Serializable> implements Serializable {

    private static final long serialVersionUID = 1L;
    
    private Map<String, Map> tabelaConfiguracao;   
    
    private Class<?> tipo;
    private Map tabela;   
    private Map tabelaType;
    private String cOrder;
    private String cOrderDirection;
    private String type;
    private String cCampoTabela;
    
    public EntityManager sessao;
    public HttpSession session;

    @SuppressWarnings("unchecked")
    public AbstractEao() {
        tabelaConfiguracao =  ListenerContextoEM.getTabelaConfiguracao();
        tipo = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public void openSessao() throws EaoException {
        this.sessao = JPAUtil.getSession();
    }

    public EntityManager getSessao() {
        return sessao;
    }

    public void setSessao(EntityManager sessao) {
        this.sessao = sessao;
    }

    public EntityManager openSessaoLote() throws EaoException {
        this.sessao = JPAUtil.getSession();
        return this.sessao;
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public void openTransaction(EntityManager sessao) throws EaoException {
        try {
            this.sessao = sessao;
            this.sessao.getTransaction().begin();
        } catch (Exception e) {
            this.sessao.getTransaction().rollback();
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        }
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public void closeTransaction(EntityManager sessao) throws EaoException {
        try {
            this.sessao = sessao;
            this.sessao.flush();
            this.sessao.clear();
            this.sessao.getTransaction().commit();
        } catch (Exception e) {
            this.sessao.getTransaction().rollback();
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            closeSessao(this.sessao);
        }
    }

    public void closeSessao(EntityManager sessao) throws EaoException {
        try {
            this.sessao = sessao;
            if (this.sessao != null) {
                this.sessao.close();
            }
        } catch (Exception e) {
            if (this.sessao.isOpen()) {
                this.sessao.getTransaction().rollback();
            }
            throw new EaoException(e.getMessage());
        } finally {
            this.sessao = null;
        }
    }

    public void closeSessaoRollback(EntityManager sessao) throws EaoException {
        try {
            this.sessao = sessao;
            if (this.sessao != null) {
                if (this.sessao.isOpen()) {
                    this.sessao.getTransaction().rollback();
                }
                this.sessao.close();
            }
        } catch (Exception e) {
            if (this.sessao.isOpen()) {
                this.sessao.getTransaction().rollback();
            }
            throw new EaoException(e.getMessage());
        } finally {
            this.sessao = null;
        }
    }

    public void create(T entidade) throws EaoException {
        try {
            create(entidade, null, null);
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
                this.sessao = null;
            }
        }
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public void create(T entidade, String cSessao, EntityManager sessao) throws EaoException {
        try {
            if (cSessao == null) {
                openSessao();
                openTransaction(this.sessao);
            } else {
                this.sessao = sessao;
            }

            this.sessao.persist(entidade);

            if (cSessao == null) {
                closeTransaction(this.sessao);
            }
        } catch (Exception e) {
            this.sessao.getTransaction().rollback();
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (cSessao == null) {
                if (this.sessao != null) {
                    this.sessao.close();
                    this.sessao = null;
                }
            }
        }
    }

    public void edit(T entidade) throws EaoException {
        edit(entidade, null, null);
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public void edit(T entidade, String cSessao, EntityManager sessao) throws EaoException {
        try {
            if (cSessao == null) {
                openSessao();
                openTransaction(this.sessao);
            } else {
                this.sessao = sessao;
            }

            this.sessao.merge(entidade);

            if (cSessao == null) {
                closeTransaction(this.sessao);
            }
        } catch (Exception e) {
            this.sessao.getTransaction().rollback();
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (cSessao == null) {
                if (this.sessao != null) {
                    this.sessao.close();
                    this.sessao = null;
                }
            }
        }
    }
    
    public void destroy(T entidade) throws EaoException {
        destroy(entidade, null, null);
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public void destroy(T entidade, String cSessao, EntityManager sessao) throws EaoException {
        try {
            if (cSessao == null) {
                openSessao();
                openTransaction(this.sessao);
            } else {
                this.sessao = sessao;
            }
            T _Entidade = this.sessao.merge(entidade);
            this.sessao.remove(_Entidade);

            if (cSessao == null) {
                closeTransaction(this.sessao);
            }
        } catch (Exception e) {
            this.sessao.getTransaction().rollback();
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (cSessao == null) {
                if (this.sessao != null) {
                    this.sessao.close();
                    this.sessao = null;
                }
            }
        }
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public final void executeUpdate(String oql, String cSessao, EntityManager sessao) throws EaoException {
        try {
            if (cSessao == null) {
                openSessao();
                this.sessao.getTransaction().begin();
            } else {
                this.sessao = sessao;
            }

            Query query = this.sessao.createNativeQuery(oql);
//            query.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
//            query.setHint("org.hibernate.cacheable", true);
            query.executeUpdate();

            if (cSessao == null) {
                this.sessao.flush();
                this.sessao.clear();
                this.sessao.getTransaction().commit();
            }
        } catch (Exception e) {
            this.sessao.getTransaction().rollback();
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (cSessao == null) {
                if (this.sessao != null) {
                    this.sessao.close();
                    this.sessao = null;
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public T getPorId(Object id) throws EaoException {
        return (T) getPorId(id, null, null);
    }

    @SuppressWarnings({"unchecked", "CallToThreadDumpStack"})
    public T getPorId(Object id, String cSessao, EntityManager sessao) throws EaoException {
        try {
            if (cSessao == null) {
                openSessao();
            } else {
                this.sessao = sessao;
            }
            return (T) this.sessao.find(this.tipo, id);
        } catch (Exception e) {
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (cSessao == null) {
                if (this.sessao != null) {
                    this.sessao.close();
                    this.sessao = null;
                }
            }
        }
    }

    // Consultada manuais dinamicas
    @SuppressWarnings(value = {"unchecked", "CallToThreadDumpStack"})
    public List<T> get(String oql) throws EaoException {
        this.sessao = JPAUtil.getSession();
        try {
            Query q = this.sessao.createQuery(oql);
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            List<T> colecao = q.getResultList();
            return colecao;
        } catch (Exception e) {
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    @SuppressWarnings(value = {"unchecked", "CallToThreadDumpStack"})
    public List<T> get(String oql, List<String> campos) throws EaoException {
        this.sessao = JPAUtil.getSession();
        try {
            Query q = this.sessao.createQuery(oql);
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            for (int ct = 0; ct < campos.size(); ++ct) {
                q.setParameter(ct + 1, campos.get(ct));
            }
            List<T> colecao = q.getResultList();
            return colecao;
        } catch (Exception e) {
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    @SuppressWarnings(value = {"unchecked", "CallToThreadDumpStack"})
    public List<T> get(String oql, Object... campos) throws EaoException {
        this.sessao = JPAUtil.getSession();
        try {
            Query q = this.sessao.createQuery(oql);
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            for (int ct = 0; ct < campos.length; ++ct) {
                q.setParameter(ct + 1, campos[ct]);
            }
            List<T> colecao = q.getResultList();
            return colecao;
        } catch (Exception e) {
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    // Consultada com query estatucas
    @SuppressWarnings(value = {"unchecked", "CallToThreadDumpStack"})
    public List<T> getPorQuery(String query) throws EaoException {
        this.sessao = JPAUtil.getSession();
        try {
            Query q = this.sessao.createNamedQuery(query);
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            List<T> colecao = q.getResultList();
            return colecao;
        } catch (Exception e) {
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    @SuppressWarnings(value = {"unchecked", "CallToThreadDumpStack"})
    public List<T> getPorQuery(String query, Object... campos)
            throws EaoException {
        this.sessao = JPAUtil.getSession();
        try {
            Query q = this.sessao.createNamedQuery(query);
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            for (int ct = 0; ct < campos.length; ++ct) {
                q.setParameter(ct + 1, campos[ct]);
            }
            List<T> colecao = q.getResultList();
            return colecao;
        } catch (Exception e) {
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    @SuppressWarnings(value = "unchecked")
    public int getCount(String entidade) {
        this.sessao = JPAUtil.getSession();
        try {
            Query q = this.sessao.createQuery("select count(o) from " + entidade + " as o");
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    @SuppressWarnings(value = "unchecked")
    public int getCount(String entidade, String where) {
        this.sessao = JPAUtil.getSession();
        try {
            Query q = this.sessao.createQuery("select count(o) from " + entidade + " as o ".concat(where));
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    @SuppressWarnings(value = "unchecked")
    public int getCountQuery(String oql) {
        this.sessao = JPAUtil.getSession();
        try {
            Query q = this.sessao.createNativeQuery(oql);
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    public Map<String, Map> getTabelaConfiguracao() {
        return tabelaConfiguracao;
    }

    @SuppressWarnings(value = "unchecked")
    public List<T> findMoedaEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = JPAUtil.getSession();
        try {
            Query q = em.createQuery("select object(o) from Moeda as o");
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    @SuppressWarnings(value = "unchecked")
    public List<T> getNative(String cTabela, int first, int pageSize, String sortField, boolean sortOrder, Map<String, String> filters, String cParametroSQL) throws EaoException {
        List<T> colecao;

        colecao = getNative(cTabela, first, pageSize, sortField, sortOrder, filters, cParametroSQL, null);
        return colecao;
    }

    @SuppressWarnings(value = {"unchecked", "CallToThreadDumpStack", "UnusedAssignment"})
    public List<T> getNative(String cTabela, int first, int pageSize, String sortField, boolean sortOrder, Map<String, String> filters, String cParametroSQL, String cFiltro) throws EaoException {
        this.sessao = JPAUtil.getSession();
        String cSql;

        FacesContext ctx = FacesContext.getCurrentInstance();
        session = (HttpSession) ctx.getExternalContext().getSession(false);
        String cSessao = (String) session.getAttribute("filtroRelatorio");
        String cSessaoOrder = (String) session.getAttribute("filtroOrder");
        
        String cRelWhere = " ";
        String cRelOrder = " ";

        try {

            if (cParametroSQL != null) {
                cSql = cParametroSQL;
            } else {
                tabela     = tabelaConfiguracao.get(cTabela); 
                tabelaType = tabelaConfiguracao.get(cTabela.concat("Type"));

                cSql = tabela.get(cTabela).toString();

                if (cFiltro != null) {
                    if (cSql.toLowerCase().contains("where")) {
                        cSql += " and ";
                    } else {
                        cSql += " where ";
                    }
                    cSql += cFiltro;
                }

                if (filters != null) {
                    if (!filters.isEmpty()) {
                        Set<String> chaves = filters.keySet();
                        for (String chave : chaves) {
                            type = (String) tabelaType.get(chave);
                            cCampoTabela = tabela.get(chave).toString();

                            if (cSql.toLowerCase().contains("where")) {
                                cSql += " and ";
                                if (cSessao != null) {
                                    cRelWhere += " and ";
                                }
                            } else {
                                cSql += " where ";
                                if (cSessao != null) {
                                    cRelWhere += " where ";
                                }
                            }

                            if (type.toLowerCase().contains("string")) {
                                cSql += "sem_acentos(".concat(cCampoTabela).concat(") like sem_acentos('%").concat(filters.get(chave)).concat("%') ");
                                if (cSessao != null) {
                                    cRelWhere += "sem_acentos(".concat(cCampoTabela).concat(") like sem_acentos('%").concat(filters.get(chave)).concat("%') ");
                                }
                            } else if (type.toLowerCase().contains("date")) {
                                cSql += "to_char(".concat(cCampoTabela).concat(", 'dd/mm/yyyy') like '%").concat(filters.get(chave)).concat("%' ");
                                if (cSessao != null) {
                                    cRelWhere += "to_char(".concat(cCampoTabela).concat(", 'dd/mm/yyyy') like '%").concat(filters.get(chave)).concat("%' ");
                                }
                            } else if (type.toLowerCase().contains("integer") || type.toLowerCase().contains("int")) {
                                //cSql += "trim(to_char(".concat(cCampoTabela).concat(",'99999')) like '%").concat(filters.get(chave)).concat("%' ");
                                cSql += cCampoTabela.concat(" = ").concat(filters.get(chave)).concat(" ");
                                if (cSessao != null) {
                                    cRelWhere += cCampoTabela.concat(" = ").concat(filters.get(chave)).concat(" ");
                                }
                            } else if (type.toLowerCase().contains("bigdecimal")) {
                                cSql += "to_char(".concat(cCampoTabela).concat(", '999,999D99') like '%").concat(filters.get(chave)).concat("%' ");
                                if (cSessao != null) {
                                    cRelWhere += "to_char(".concat(cCampoTabela).concat(", '999,999D99') like '%").concat(filters.get(chave)).concat("%' ");
                                }
                            }
                        }
                    }
                }

                if (sortField != null) {
                    cOrder = (String) tabela.get(sortField);
                } else {
                    cOrder = (String) tabela.get("Order");
                }

                if (sortOrder) {
                    cOrderDirection = "asc";
                } else {
                    cOrderDirection = "desc";
                }

                if (cSessaoOrder != null) {
                    cRelOrder = " order by ".concat(cOrder).concat(" ").concat(cOrderDirection);
                    session.setAttribute("filtroOrder", cRelOrder);
                }

                cSql += " order by ".concat(cOrder).concat(" ").concat(cOrderDirection);

                cSql += " limit " + pageSize + " offset " + first;
            }
            if (cSessao != null) {
                session.setAttribute("filtroRelatorio", cRelWhere);
            }

            Query q = this.sessao.createNativeQuery(cSql, this.tipo);
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            List<T> colecao = q.getResultList();
            return colecao;
        } catch (Exception e) {
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }

    @SuppressWarnings(value = {"unchecked", "CallToThreadDumpStack"})
    public T getNativeFiltro(String cSql) throws EaoException {
        this.sessao = JPAUtil.getSession();

        try {
            Query q = this.sessao.createNativeQuery(cSql, this.tipo);
            q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
            q.setHint("org.hibernate.cacheable", true);
            T colecao = (T) q.getResultList().get(0);
            return colecao;
        } catch (Exception e) {
            e.printStackTrace();
            throw new EaoException(e.getMessage());
        } finally {
            if (this.sessao != null) {
                this.sessao.close();
            }
        }
    }
}